package org.example.util;

import com.google.common.base.Strings;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * 可以设置有效期的内存缓存
 *
 * @param <K>
 * @param <V>
 * @author sxt
 */
public class ExpiringCache<K, V> {

    private final Map<K, V> cache;

    private final Map<K, V> wasteCache;

    private final Map<K, ScheduledFuture<?>> schedulerMap;

    private final ScheduledExecutorService scheduler;

    public ExpiringCache() {
        cache = new ConcurrentHashMap<>();
        wasteCache = new ConcurrentHashMap<>();
        scheduler = Executors.newSingleThreadScheduledExecutor();
        schedulerMap = new ConcurrentHashMap<>();
    }

    public boolean put(K key, V value, long expirationTimeMillis) {
        if (cache.containsKey(key)) {
            return false;
        }
        cache.put(key, value);
        scheduleExpiration(key, expirationTimeMillis);
        return true;
    }

    public boolean refreshExpire(K key, long expirationTimeMillis) {
        if (!cache.containsKey(key) && !wasteCache.containsKey(key)) {
            return false;
        }
        // 刷新有效期
        boolean rs = clearMayHaveScheduled(key);
        if (!rs) {
            return false;
        }
        scheduleExpiration(key, expirationTimeMillis);
        return true;
    }

    public boolean refreshExpireByCondition(V condition, long expirationTimeMillis) {
        K foundKey = findValueByCondition(condition, cache);
        if (foundKey == null) {
            foundKey = findValueByCondition(condition, wasteCache);
        }
        if (foundKey == null) {
            return false;
        }
        return this.refreshExpire(foundKey, expirationTimeMillis);
    }

    private boolean clearMayHaveScheduled(K key) {
        ScheduledFuture<?> scheduledFuture = schedulerMap.get(key);
        if (scheduledFuture == null) {
            // 检查一下wasteCache里有没有对应的key的内容 若有，拿出来，放回去
            V v = wasteCache.get(key);
            if (v != null) {
                cache.put(key, v);
                wasteCache.remove(key);
                return true;
            }
            return false;
        }
        // cancel 之前 scheduled 已经 执行完了!!!
        scheduledFuture.cancel(false);
        // 如果scheduled 还没有执行，cancel之后一定不会再执行了
        // 检查一下wasteCache里有没有对应的key的内容 若有，拿出来，放回去
        V v = wasteCache.get(key);
        if (v != null) {
            cache.put(key, v);
            wasteCache.remove(key);
        }
        schedulerMap.remove(key);
        return true;
    }

    public V get(K key) {
        return cache.get(key);
    }

    public List<V> getByValueField(String valueField, Object value) {
        if (Strings.isNullOrEmpty(valueField) || Objects.isNull(value)) {
            return new ArrayList<>();
        }
        List<V> result = new ArrayList<>();
        for (K key : cache.keySet()) {
            V v = cache.get(key);
            List<Field> allFields = ReflectUtil.getAllFields(v.getClass());
            Optional<Field> fieldOptional = allFields.stream().filter(field -> field.getName().equals(valueField)).findAny();
            fieldOptional.ifPresent(field -> {
                try {
                    field.setAccessible(true);
                    Object o = field.get(v);
                    if (value.equals(o)) {
                        result.add(v);
                    }
                } catch (IllegalAccessException e) {
                    // do nothing
                }

            });
        }
        return result;
    }

    public boolean remove(K key) {
        if (!cache.containsKey(key) && !wasteCache.containsKey(key)) {
            return false;
        }
        clearMayHaveScheduled(key);
        cache.remove(key);
        return true;
    }

    public boolean removeByCondition(V condition) {
        K foundKey = findValueByCondition(condition, cache);
        if (foundKey == null) {
            foundKey = findValueByCondition(condition, wasteCache);
        }
        if (foundKey == null) {
            return false;
        }
        return this.remove(foundKey);
    }

    private K findValueByCondition(V condition, Map<K, V> map) {
        // k v, k v k null
        Map<Field, Object> conditionMap = ReflectUtil.getAllFields(condition.getClass())
                .stream()
                .filter(field -> {
                    if (Objects.isNull(field)) {
                        return false;
                    }
                    field.setAccessible(true);
                    try {
                        if (Objects.isNull(field.get(condition))) {
                            return false;
                        }
                    } catch (Exception e) {
                        return false;
                    }
                    return true;
                })
                .collect(Collectors.toMap(field -> field, field -> {
                    field.setAccessible(true);
                    try {
                        return field.get(condition);
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }, (o, o2) -> o));

        for (K key : map.keySet()) {
            // Service
            V value = map.get(key);
            boolean find = true;
            for (Field field : conditionMap.keySet()) {
                Object rightVal = conditionMap.get(field);
                try {
                    Object leftVal = field.get(value);
                    if (!rightVal.equals(leftVal)) {
                        find = false;
                    }
                } catch (IllegalAccessException e) {
                    find = false;
                }
            }
            if (find) {
                return key;
            }
        }
        return null;
    }

    public List<V> getAll() {
        return new ArrayList<>(cache.values());
    }

    private void scheduleExpiration(K key, long expirationTimeMillis) {
        schedulerMap.put(key, scheduler.schedule(() -> {
            wasteCache.put(key, cache.get(key));
            cache.remove(key);
            System.out.println("Key expired: " + key);
            schedulerMap.remove(key);
        }, expirationTimeMillis, TimeUnit.MILLISECONDS));
    }

    public void shutdown() {
        scheduler.shutdown();
    }

    public static void main(String[] args) {
        ExpiringCache<String, String> cache = new ExpiringCache<>();
        cache.put("key1", "value1", 3000); // 设置有效期为3秒

        // 测试获取缓存值
        System.out.println(cache.get("key1")); // 输出: value1

        try {
            Thread.sleep(4000); // 等待4秒，超过有效期
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 再次尝试获取缓存值，应该返回null
        System.out.println(cache.get("key1")); // 输出: null

        cache.shutdown(); // 关闭缓存
    }
}
